﻿using System;
using System.Drawing;
using System.IO;

namespace Module.FlowChart.Model
{
    public enum Shape
    {
        Non,
        Rect,
        Circular,
        Diamond,
        Parallelogram
    }
    public class FCNode
    {

        //共用数据
        private string str1;
        private string str2;
        private bool b1;

        //样式信息
        public Font Font;
        public Color BackGroundColor;


        //基础信息
        public FCNodeType Type;
        public Shape Shape;
        public int Id;
        public int Next;
        public Rectangle LogicRect;
        public string Describe;



        #region NormalNode
        public string LeftExp { get => str1; set => str1 = value; }
        public string RightExp { get => str2; set => str2 = value; }
        #endregion
        #region TerminalNode
        public string FuncName { get => str1; set => str1 = value; }
        public string ReturnExp { get => str2; set => str2 = value; }
        public string Params { get => str2; set => str2 = value; }
        public bool IsStartNode { get => b1; set => b1 = value; }
        #endregion
        #region IFNode 
        public string ConditionExp { get => str1; set => str1 = value; }
        public int TrueNext { get => Next; set => Next = value; }
        public int FalseNext;
        #endregion
        #region IONode
        public bool IsOutPut { get => b1; set => b1 = value; }
        public string IOExp { get => str1; set => str1 = value; }
        #endregion
        #region LabelNode
        public string StringLabel { get => str1; set => str1 = value; }
        #endregion


        //View信息
        public Rectangle RealRect;//节点在容器中的实际大小
        public bool IsInWindow;//节点在容器中是否可见
        public bool Visible;//是否显示


        public FCNode(FCNodeType type, Rectangle logicrect)
        {
            Type = type;
            LogicRect = logicrect;
            Next = 0;
            Visible = true;

            str1 = str2 = ""; b1 = false;

            Describe = "";
            BackGroundColor = Color.Yellow;
            Font = new Font("宋体", 15);
        }
        /// <summary>
        /// /创建一个终结点
        /// </summary>
        public FCNode(Rectangle logicrect, bool isstartnode, string name = "Main") : this(FCNodeType.Terminal, logicrect)
        {
            Shape = Shape.Circular;
            IsStartNode = isstartnode;
            FuncName = name;
        }
        /// <summary>
        /// 创建一个赋值节点
        /// </summary>
        public FCNode(Rectangle logicrect, string leftexp, string rightexp, int next)
        : this(FCNodeType.Assign, logicrect)
        {
            Shape = Shape.Rect;
            LeftExp = leftexp;
            RightExp = rightexp;
        }
        /// <summary>
        /// 创建一个条件节点
        /// </summary>
        public FCNode(Rectangle logicrect, string condition, int truenext, int falsenext)
       : this(FCNodeType.IF, logicrect)
        {
            Shape = Shape.Diamond;
            ConditionExp = condition;
            TrueNext = truenext;
            FalseNext = falsenext;
        }
        /// <summary>
        /// 创建一个IO节点
        /// </summary>
        public FCNode(Rectangle logicrect, string exp, bool isoutput)
       : this(FCNodeType.IO, logicrect)
        {
            Shape = Shape.Parallelogram;
            IOExp = exp;
            IsOutPut = isoutput;
        }
        /// <summary>
        /// 创建一个描述节点
        /// </summary>
        public FCNode(Rectangle logicrect, Shape shape, string describe) : this(FCNodeType.Label, logicrect)
        {
            Shape = shape;
            StringLabel = describe ?? "";
        }

        public override string ToString()
        {
            switch (Type)
            {
                case FCNodeType.Non:
                    return "Non";
                case FCNodeType.Terminal:
                    return IsStartNode
                       ? (FuncName == "" ? "Start" : "S:" + FuncName)
                       : (ReturnExp == "" ? "End" : "R:" + ReturnExp);
                case FCNodeType.Assign:
                    return LeftExp + " = " + RightExp;
                case FCNodeType.IF:
                    return ConditionExp + " ? ";
                case FCNodeType.IO:
                    return IsOutPut ? "OUT : " + IOExp : "IN : " + IOExp;
                case FCNodeType.Label:
                    return StringLabel;
                default:
                    break;
            }
            return base.ToString();
        }
        public string GetNodeText()
        {
            switch (Type)
            {
                case FCNodeType.Non:
                    return "Non";
                case FCNodeType.Terminal:
                    return IsStartNode
                        ? (FuncName == "" ? "Start" : "S:" + FuncName)
                        : (ReturnExp == "" ? "End" : "R:" + ReturnExp);
                case FCNodeType.Assign:
                    return LeftExp + " = " + RightExp;
                case FCNodeType.IF:
                    return ConditionExp == "" ? "" : ConditionExp + " ? ";
                case FCNodeType.IO:
                    return IsOutPut ? "OUT : " + IOExp : "IN : " + IOExp;
                case FCNodeType.Label:
                    return StringLabel;
                default:
                    break;
            }
            return base.ToString();
        }

    }
    public class FCLine
    {
        //基础信息
        public FCLineType Type;
        public int From, To;
        public int Anchor1, Anchor2;
        public bool Link1;
        public Point[] Points;
        public int Id;
        public string Text;

        //View信息
        public Point[] RealPoints;
        public bool IsInWindow;//在容器中是否可见
        public bool Visible;//是否显示

        public FCLine()
        {
            Visible = true; Text = "";
        }
        public FCLine(int from, int to, int anchor1, int anchor2, Point[] points, bool link1 = true, FCLineType type = FCLineType.Broken) : this()
        {
            Type = type;
            From = from;
            Anchor1 = anchor1;
            Anchor2 = anchor2;
            To = to;
            Points = points;
            RealPoints = new Point[points.Length];
            Link1 = link1;
            Text = "";
        }

        public string GetLineText()
        {
            return Text;
        }
    }


    public enum FCNodeType
    {
        Non,
        Terminal,
        Assign,
        IF,
        IO,
        Label,
    }
    public enum FCLineType
    {
        Non,
        Broken,
    }

    public static partial class EX
    {
        static public FCNode ReadFCNode(this BinaryReader br)
        {
            byte bv; int id;
            FCNode node;

            Shape shape;
            id = br.ReadInt32();//id
            bv = br.ReadByte();//type
            shape = (Shape)br.ReadByte();//shap2
            Rectangle rect = br.ReadRectangle();//logicrect
            node = new FCNode((FCNodeType)bv, rect);
            node.Id = id;
            node.Shape = shape;
            switch ((FCNodeType)bv)
            {
                case FCNodeType.Non:
                    throw new Exception("Unexpected node");
                case FCNodeType.Terminal:
                    node.IsStartNode = br.ReadBoolean();
                    if (node.IsStartNode)
                    {
                        node.FuncName = br.ReadString();
                        node.Params = br.ReadString();
                    }
                    else
                        node.ReturnExp = br.ReadString();
                    node.Next = br.ReadInt32();
                    break;
                case FCNodeType.Assign:
                    node.LeftExp = br.ReadString();
                    node.RightExp = br.ReadString(); ;
                    node.Next = br.ReadInt32();
                    break;
                case FCNodeType.IF:
                    node.ConditionExp = br.ReadString();
                    node.TrueNext = br.ReadInt32();
                    node.FalseNext = br.ReadInt32();
                    break;
                case FCNodeType.IO:
                    node.IOExp = br.ReadString();
                    node.IsOutPut = br.ReadBoolean();
                    node.Next = br.ReadInt32();
                    break;
                case FCNodeType.Label:
                    node.StringLabel = br.ReadString();
                    break;
                default:
                    break;
            }
            node.Describe = br.ReadString();
            node.Font = br.ReadFont();
            node.BackGroundColor = br.ReadColor();
            return node;
        }
        static public void Write(this BinaryWriter bw, FCNode node)
        {
            bw.Write(node.Id);
            bw.Write((byte)node.Type);
            bw.Write((byte)node.Shape);
            bw.Write(node.LogicRect);
            switch (node.Type)
            {
                case FCNodeType.Terminal:
                    bw.Write(node.IsStartNode);
                    if (node.IsStartNode)
                    {
                        bw.Write(node.FuncName);
                        bw.Write(node.Params);
                    }
                    else
                        bw.Write(node.ReturnExp);
                    bw.Write(node.Next);
                    break;
                case FCNodeType.Assign:
                    bw.Write(node.LeftExp);
                    bw.Write(node.RightExp);
                    bw.Write(node.Next);
                    break;
                case FCNodeType.IF:
                    bw.Write(node.ConditionExp);
                    bw.Write(node.TrueNext);
                    bw.Write(node.FalseNext);
                    break;
                case FCNodeType.IO:
                    bw.Write(node.IOExp);
                    bw.Write(node.IsOutPut);
                    bw.Write(node.Next);
                    break;
                case FCNodeType.Label:
                    bw.Write(node.StringLabel);
                    break;
                default:
                    throw new Exception("Unexpected node");
            }

            bw.Write(node.Describe);
            bw.Write(node.Font);
            bw.Write(node.BackGroundColor);
        }
        static public FCLine ReadFCLine(this BinaryReader br)
        {
            FCLine line = new FCLine();
            line.Id = br.ReadInt32();
            line.Type = (FCLineType)br.ReadByte();
            line.From = br.ReadInt32(); line.Anchor1 = br.ReadInt32();
            line.To = br.ReadInt32(); line.Anchor2 = br.ReadInt32();
            line.Link1 = br.ReadBoolean();
            line.Text = br.ReadString();
            switch (line.Type)
            {
                case FCLineType.Non:
                    break;
                case FCLineType.Broken:
                    int v = br.ReadInt32();
                    if (v != 0)
                    {
                        line.Points = new Point[v];
                        line.RealPoints = new Point[v];
                        for (int j = 0; j < v; j++)
                        {
                            line.Points[j] = br.ReadPoint();
                        }
                    }
                    break;
                default:
                    break;
            }
            return line;
        }
        static public void Write(this BinaryWriter bw, FCLine line)
        {
            bw.Write(line.Id);
            bw.Write((byte)line.Type);
            bw.Write(line.From); bw.Write(line.Anchor1);
            bw.Write(line.To); bw.Write(line.Anchor2); ; bw.Write(line.Link1);
            bw.Write(line.Text);
            switch (line.Type)
            {
                case FCLineType.Non:
                    break;
                case FCLineType.Broken:
                    if (line.Points == null)
                    {
                        bw.Write(0);
                    }
                    else
                    {
                        bw.Write(line.Points.Length);
                        for (int j = 0; j < line.Points.Length; j++)
                        {
                            bw.Write(line.Points[j]);
                        }
                    }
                    break;
                default:
                    break;
            }
        }
    }
}

namespace Module.FlowChart.Logic
{
    public class LineManager
    {
        public const int DefaultLineLengthOutNode = 30;

        /// <summary>
        /// anchorid 1 上 2 右 3下 4 左
        /// </summary>
        static public Point GetAnchorPoint(Rectangle realrect, int anchorid)
        {
            switch (anchorid)
            {
                case 1:
                    return new Point(realrect.X + realrect.Width / 2, realrect.Y);
                case 2:
                    return new Point(realrect.X + realrect.Width, realrect.Y + realrect.Height / 2);
                case 3:
                    return new Point(realrect.X + realrect.Width / 2, realrect.Y + realrect.Height);
                case 4:
                    return new Point(realrect.X, realrect.Y + realrect.Height / 2);
                default:
                    return new Point(0, 0);
            }
        }
        static public Point[] GetLinePoints(Rectangle rect1, int anchor1, Point end, int default_margin_x = DefaultLineLengthOutNode, int default_margin_y = DefaultLineLengthOutNode)
        {
            if (anchor1 == 1)
            {
                Point s = GetAnchorPoint(rect1, anchor1);
                if (s.Y > end.Y)
                {
                    if (s.X == end.X)
                        return new Point[] { s, end };
                    return new Point[] { s, new Point(s.X, (s.Y + end.Y) / 2), new Point(end.X, (s.Y + end.Y) / 2), end };
                }
                else
                {
                    if (end.X < rect1.X || end.X > rect1.X + rect1.X)
                    {//左右
                        return new Point[] { s, new Point(s.X, s.Y - default_margin_y), new Point(end.X, s.Y - default_margin_y), end };
                    }
                    else
                    {//下方
                        int x = Math.Min(end.X, s.X) - default_margin_x;
                        return new Point[] { s, new Point(s.X, s.Y - default_margin_y), new Point(x, s.Y - default_margin_y), new Point(x, end.Y), end };
                    }
                }

            }
            else if (anchor1 == 2)
            {
                Point margin = new Point(default_margin_x, default_margin_y).Rotate_270();
                Point[] points = GetLinePoints(rect1.Rotate_270(), 1, end.Rotate_270(), margin.X, margin.Y);
                for (int i = 0; i < points.Length; i++)
                {
                    points[i] = points[i].Rotate_90();
                }
                return points;
            }
            else if (anchor1 == 3)
            {
                Point margin = new Point(default_margin_x, default_margin_y).Rotate_180();
                Point[] points = GetLinePoints(rect1.Rotate_180(), 1, end.Rotate_180(), margin.X, margin.Y);
                for (int i = 0; i < points.Length; i++)
                {
                    points[i] = points[i].Rotate_180();
                }
                return points;
            }
            else
            {
                Point margin = new Point(default_margin_x, default_margin_y).Rotate_90();
                Point[] points = GetLinePoints(rect1.Rotate_90(), 1, end.Rotate_90(), margin.X, margin.Y);
                for (int i = 0; i < points.Length; i++)
                {
                    points[i] = points[i].Rotate_270();
                }
                return points;
            }
        }
        static public Point[] GetLinePoints(Rectangle rect1, int anchor1, Rectangle rect2, int anchor2, int default_margin_x = DefaultLineLengthOutNode, int default_margin_y = DefaultLineLengthOutNode)
        {

            if (anchor1 == 1)
            {
                Point s = GetAnchorPoint(rect1, anchor1);
                Point e = GetAnchorPoint(rect2, anchor2);
                if (anchor2 == 1)
                {
                    int y = s.Y < e.Y ? s.Y - default_margin_y : e.Y - default_margin_y;
                    return new Point[] { s, new Point(s.X, y), new Point(e.X, y), e };
                }
                else if (anchor2 == 2)
                {
                    if (e.Y < s.Y)
                    {
                        if (e.X > s.X)
                        {//类42
                            int y1 = (rect2.Y + rect2.Height + rect1.Y) / 2;
                            return new Point[] { s, new Point(s.X, y1), new Point(e.X + default_margin_x, y1), new Point(e.X + default_margin_x, e.Y), e };
                        }
                        else
                        {//类41
                            return new Point[] { s, new Point(s.X, e.Y), e };
                        }
                    }
                    else
                    {
                        if (rect2.X + rect2.Width < rect1.X)
                        {//类43
                            int x = (rect2.X + rect2.Width + rect1.X) / 2;
                            return new Point[] { s, new Point(s.X, s.Y - default_margin_y), new Point(x, s.Y - default_margin_y), new Point(x, e.Y), e };
                        }
                        else
                        {//类44
                            int x = Math.Max(rect1.X + rect1.Width, rect2.X + rect2.Width) + default_margin_x;
                            return new Point[] { s, new Point(s.X, s.Y - default_margin_y), new Point(x, s.Y - default_margin_y), new Point(x, e.Y), e };
                        }
                    }
                }
                else if (anchor2 == 3)
                {
                    if (e.Y < s.Y)
                    {//上方
                        if (e.X == s.X)
                        {//正上方 31
                            return new Point[] { s, e };
                        }
                        else
                        {//上方 32
                            int y = (s.Y + e.Y) / 2;
                            return new Point[] { s, new Point(s.X, y), new Point(e.X, y), e };
                        }

                    }
                    else if (rect2.X + rect2.Width < rect1.X)
                    {//左边 X轴无重叠33
                        int x = (rect2.X + rect2.Width + rect1.X) / 2;
                        return new Point[] { s, new Point(s.X, s.Y - default_margin_y), new Point(x, s.Y - default_margin_y), new Point(x, e.Y + default_margin_y), new Point(e.X, e.Y + default_margin_y), e };
                    }
                    else if (rect2.X > rect1.X + rect1.Width)
                    {//右边 X轴无重叠34
                        int x = (rect2.X + rect1.X + rect1.Width) / 2;
                        return new Point[] { s, new Point(s.X, s.Y - default_margin_y), new Point(x, s.Y - default_margin_y), new Point(x, e.Y + default_margin_y), new Point(e.X, e.Y + default_margin_y), e };
                    }
                    else
                    {// X轴重叠35
                        int y1 = Math.Min(rect2.Y, rect1.Y) - default_margin_y;
                        int x1 = Math.Min(rect2.X, rect1.X) - default_margin_x;
                        int y2 = Math.Max(rect2.Y + rect2.Height, rect1.Y + rect1.Height) + default_margin_y;
                        return new Point[] { s, new Point(s.X, y1), new Point(x1, y1), new Point(x1, y2), new Point(e.X, y2), e };
                    }
                }
                else//4
                {
                    if (e.Y < s.Y)
                    {
                        if (e.X < s.X)
                        {//42
                            int y1 = (rect2.Y + rect2.Height + rect1.Y) / 2;
                            return new Point[] { s, new Point(s.X, y1), new Point(rect2.X - default_margin_x, y1), new Point(rect2.X - default_margin_x, e.Y), e };
                        }
                        else
                        {//41
                            return new Point[] { s, new Point(s.X, e.Y), e };
                        }
                    }
                    else
                    {
                        if (rect1.X + rect1.Width < rect2.X)
                        {//43
                            int x = (rect2.X + rect1.X + rect1.Width) / 2;
                            return new Point[] { s, new Point(s.X, s.Y - default_margin_y), new Point(x, s.Y - default_margin_y), new Point(x, e.Y), e };
                        }
                        else
                        {//44
                            int x = Math.Min(rect1.X, rect2.X) - default_margin_x;
                            return new Point[] { s, new Point(s.X, s.Y - default_margin_y), new Point(x, s.Y - default_margin_y), new Point(x, e.Y), e };
                        }
                    }

                }
            }
            else if (anchor1 == 2)
            {
                int a = anchor2 - anchor1 + 1 <= 0 ? anchor2 - anchor1 + 1 + 4 : anchor2 - anchor1 + 1;
                Point[] points = GetLinePoints(rect1.Rotate_270(), 1, rect2.Rotate_270(), a, default_margin_x, default_margin_y);
                for (int i = 0; i < points.Length; i++)
                {
                    points[i] = points[i].Rotate_90();
                }
                return points;
            }
            else if (anchor1 == 3)
            {
                int a = anchor2 - anchor1 + 1 <= 0 ? anchor2 - anchor1 + 1 + 4 : anchor2 - anchor1 + 1;
                Point[] points = GetLinePoints(rect1.Rotate_180(), 1, rect2.Rotate_180(), a, default_margin_x, default_margin_y);
                for (int i = 0; i < points.Length; i++)
                {
                    points[i] = points[i].Rotate_180();
                }
                return points;
            }
            else
            {
                int a = anchor2 - anchor1 + 1 <= 0 ? anchor2 - anchor1 + 1 + 4 : anchor2 - anchor1 + 1;
                Point[] points = GetLinePoints(rect1.Rotate_90(), 1, rect2.Rotate_90(), a, default_margin_x, default_margin_y);
                for (int i = 0; i < points.Length; i++)
                {
                    points[i] = points[i].Rotate_270();
                }
                return points;
            }
        }
    }
}